package com.seckillpro.seckill_pro.controller;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.seckillpro.seckill_pro.exception.GlobalException;
import com.seckillpro.seckill_pro.pojo.SecKillMessage;
import com.seckillpro.seckill_pro.pojo.TOrder;
import com.seckillpro.seckill_pro.pojo.TSecKillOrder;
import com.seckillpro.seckill_pro.pojo.TUser;
import com.seckillpro.seckill_pro.rabbtmq.MQSender;
import com.seckillpro.seckill_pro.service.ITGoodsService;
import com.seckillpro.seckill_pro.service.ITOrderService;
import com.seckillpro.seckill_pro.service.ITSecKillOrderService;
import com.seckillpro.seckill_pro.vo.GoodsVo;
import com.seckillpro.seckill_pro.vo.RespBean;
import com.seckillpro.seckill_pro.vo.RespBeanEnum;
import com.wf.captcha.ArithmeticCaptcha;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/* @Package: com.seckillpro.seckill_pro.controller
 * @ClassName: SecKillController
 * @Author: lyx
 * @CreateTime: 2021/3/10 17:12
 * @Description:
 */

@Slf4j
@Controller
@RequestMapping("/secKill")
public class SecKillController implements InitializingBean {

    @Autowired
    private ITGoodsService goodsService;
    @Autowired
    private ITSecKillOrderService secKillOrderService;
    @Autowired
    private ITOrderService orderService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MQSender mqSender;
    @Autowired
    private RedisScript<Long> redisScript;

    private Map<Long, Boolean> EmptyStockMap = new HashMap<Long, Boolean>();

    @RequestMapping("/doSecKill2")
    public String doSecKill2(Model model, TUser user, Long goodId) {

        if (user == null) {
            return "login";
        }
        model.addAttribute("user", user);
        GoodsVo goods = goodsService.findGoodsVoByGoodId(goodId);
        if (goods.getStockCount() < 1) {
            model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());
            return "secKillFail";
        }

        //判断是否重复抢购
        TSecKillOrder secKillOrder = secKillOrderService.getOne(new QueryWrapper<TSecKillOrder>().eq("user_id", user.getId()).eq("good_id", goodId));

        if (secKillOrder != null) {
            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
            return "secKillFail";
        }

        TOrder order = orderService.secKill(user, goods);
        model.addAttribute("order", order);
        model.addAttribute("goods", goods);
        return "orderDetail";
    }


    /**
     * 优化后的秒杀接口
     *
     * @param model
     * @param user
     * @param goodId
     * @return
     */

    @RequestMapping(value = "/{path}/doSecKill", method = RequestMethod.POST)
    @ResponseBody
    public RespBean doSecKill(@PathVariable String path, TUser user, Long goodId) {

        System.out.println(path);
        if (ObjectUtil.isEmpty(user)) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }

        //获取redisTemplate
        ValueOperations valueOperations = redisTemplate.opsForValue();
        boolean check = orderService.checkPath(user, goodId, path);
        if (!check) {
            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
        }

        //判断是否重复抢购
        TSecKillOrder secKillOrder =
                (TSecKillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodId);

        if (secKillOrder != null) {
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }

        //内存标记，减少redis的访问
        if (EmptyStockMap.get(goodId)) {
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }
        //预减操作，调用一次减一（原子性的）
        Long stock = valueOperations.decrement("secKillGood:" + goodId);
//        Long stock =
//                (Long) redisTemplate.execute(redisScript, Collections.singletonList("secKillGoods:" + goodId), Collections.EMPTY_LIST);
        if (stock < 0) {
            EmptyStockMap.put(goodId, true);
            valueOperations.increment("secKillGood:" + goodId);
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }


        SecKillMessage seckillMessage = new SecKillMessage(user, goodId);
        mqSender.sendSeckillMessage(JSONUtil.toJsonStr(seckillMessage));
        return RespBean.success(0);


        /*
        model.addAttribute("user", user);
        GoodsVo goods = goodsService.findGoodsVoByGoodId(goodId);
        if (goods.getStockCount() < 1) {
            model.addAttribute("errmsg", RespBeanEnum.EMPTY_STOCK.getMessage());
            return RespBean.error(RespBeanEnum.EMPTY_STOCK);
        }

        //判断是否重复抢购
//        TSecKillOrder secKillOrder = secKillOrderService.getOne(new QueryWrapper<TSecKillOrder>().eq("user_id", user.getId()).eq("good_id", goodId));
        TSecKillOrder secKillOrder = (TSecKillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goods.getId());

        if (secKillOrder != null) {
            model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }

        TOrder order = orderService.secKill(user, goods);
        return RespBean.success(order);

         */

    }


    /**
     * 获取秒杀结果
     * return  orderId:成功 -1 秒杀失败，  0 排队中
     *
     * @param user
     * @param goodId
     * @return
     */
    @RequestMapping(value = "/getResult", method = RequestMethod.GET)
    @ResponseBody
    public RespBean getResult(TUser user, Long goodId) {

        if (ObjectUtil.isEmpty(user)) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }

        Long orderId = secKillOrderService.getResult(user, goodId);
        return RespBean.success(orderId);

    }

    /**
     * 初始化执行的方法，将商品加载到redis中
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet()  {
        List<GoodsVo> list = goodsService.findGoodsVoList();
        if (CollectionUtil.isEmpty(list)) {
            return;
        }

        list.forEach(goodsVo -> {
            redisTemplate.opsForValue().set("secKillGood:" + goodsVo.getId(), goodsVo.getStockCount());
            EmptyStockMap.put(goodsVo.getId(), false);
        });
    }


    /**
     * 获取路径
     *
     * @param user
     * @param goodId
     * @return
     */
    @RequestMapping(value = "/path", method = RequestMethod.GET)
    @ResponseBody
    public RespBean getPath(TUser user, Long goodId, String captcha, HttpServletRequest request) {
        if (ObjectUtil.isEmpty(user)) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        boolean check = orderService.checkCaptcha(user, goodId, captcha);

        //限流操作,5秒钟访问五次
        ValueOperations valueOperations = redisTemplate.opsForValue();

        String uri = request.getRequestURI();
        Integer count = (Integer) valueOperations.get(uri + ":" + user.getId());

        if (count == null) {
            valueOperations.set(uri + ":" + user.getId(), 1, 5, TimeUnit.SECONDS);
        } else if (count < 5) {
            valueOperations.increment(uri + ":" + user.getId());
        } else {
            return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REACHED);
        }

        if (!check) {
            return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
        }
        String str = orderService.createPath(user, goodId);
        System.out.println(str);
        return RespBean.success(str);
    }

    @RequestMapping(value = "/captcha", method = RequestMethod.GET)
    public void verifyCode(TUser user, Long goodId, HttpServletResponse response) {

        if (ObjectUtil.isEmpty(user) || goodId < 0) {
            throw new GlobalException(RespBeanEnum.REQUEST_ILLEGAL);
        }
        //设置请求头为输出图片的类型
        response.setContentType("images/jpg");
        response.setHeader("Pargam", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        //生成验证码，将结果放入Redis
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 32, 3);
        redisTemplate.opsForValue().set("captcha:" + user.getId() + ":" + goodId, captcha.text(), 300,
                TimeUnit.SECONDS);

        try {
            captcha.out(response.getOutputStream());
        } catch (IOException e) {
            log.error("验证码生成失败", e.getMessage());
        }
    }

}

